package monarc.jscheduler.dag;

import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import java.util.Vector;

import monarc.center.Farm;
import monarc.jscheduler.dag.graph.Nod;
import monarc.jscheduler.dag.MonitorJob;

import edu.uci.ics.jung.graph.impl.DirectedSparseGraph;

/**
 * 
 * @author Alexandra Olteanu
 *
 * Node status catalog for a graph
 */


public class StatusCatalog {

	/**
	 *  1 - created
	 *  2 - submitted
	 *  3 - running
	 *  4 - finished
	 *  0 - error
	 */
	public Vector<Integer> taskStatus;
	public Vector<Nod> Nods;
	public DirectedSparseGraph tasks;
	private Integer init = 1;
	private Integer error = 0;
	private Integer submitted = 2;
	private Integer running = 3;
	private Integer finished = 4;
	public Integer set = 0;
	public MonitorJob monitor;
	
	/** test vector */
	private Vector<Integer> testVec;
	private Integer pos = 0;
	private Integer aux = 0;
	
	/** 
	 * The constructor. 
	 */ 
	
	/**
	 * constructor catalog
	 */
	
	public StatusCatalog(){
		this.taskStatus = new Vector<Integer>();
		this.Nods = new Vector<Nod>();		
	}
	
	/** 
	 * The constructor. 
	 * @param g The Direct Graph of the tasks 
	 */ 
	
	/**
	 * constructor catalog
	 * @param g graful orientat de taskuri
	 */
	public StatusCatalog( DirectedSparseGraph g)
	{
		if(this.set == 0){  //daca nu a fost deja construit
			this.tasks = g;
			this.Nods = new Vector<Nod>();
			Set noduri = g.getVertices();		
			Iterator iter = noduri.iterator();
			while ( iter.hasNext() )
			{
				Nods.add((Nod)iter.next());
			}
			
			this.taskStatus = new Vector<Integer>();
			for ( int i = 0; i < Nods.size(); i++ )
			{
				taskStatus.add(i,init);
			}
			
			this.set = 1;
		}
	}
	
	/** 
	 * This method add a new status node for index index  
	 * @param index node index
	 * @param status node status
	 */ 
	
	/**
	 * adauga statusul
	 */
	

	public void addStatus(Integer index,int status){
		taskStatus.setElementAt(index, status);
		
	}
	
	/** 
	 * This method set all nodes in graph to status state  
	 */ 
	
	/**
	 * pune statusul pe status pentru toate nodurile din graf
	 */
	public void resetDAGStatus(Integer status){
		
		for ( int i = 0; i < Nods.size(); i++ )
			taskStatus.setElementAt(status,Nods.get(i).index);	
	}
	
	/**
	 * This method sets all nodes in a sub-graph with root node to status state
	 * @param node the root node of sub-graph
	 */
	
	/**
	 * reseteaza statusul unei parti din graf ce are ca nod root node
	 * @param node nodul root al subgrafului 
	 */
	public void resetPartialDAGStatus(Nod node, Integer status){
		Set succ = node.getSuccessors();
		Iterator iter = succ.iterator();
		while ( iter.hasNext() )
		{
			Nod urmas = (Nod)iter.next();
			taskStatus.setElementAt(status,urmas.index);
		}
		
	}

	/**
	 * This method reset the state for a subgraph
	 * @param g the subgraph
	 * @param status 
	 */
	public void resetPartialDAGStatus(DirectedSparseGraph g, Integer status){
		Set noduri = g.getVertices();		
		Iterator iter = noduri.iterator();
		while ( iter.hasNext() )
		{
			Nod n =(Nod)iter.next();
			taskStatus.setElementAt(status,n.index);
		}
	}
	
	/**
	 * This method change the node status 
	 * @param index the node index
	 * @param status the new node status 
	 */
	
	/**
	 * schimba statusul unui nod
	 * @param index indexul nodului
	 * @param status statusul nodului
	 */
	public void changeStatus(Integer index, Integer status){
		if(taskStatus.get(index)!= finished){
			taskStatus.setElementAt(status,index);
		}
	}
	
	/**
	 * This method change the node status to error
	 * @param index the node index
	 */
	
	/**
	 * setez statusul pe eroare
	 * @param index indexul nodului
	 */
	public void setStatusError(Integer index){
		
		if(taskStatus.get(index)!= finished)
			taskStatus.setElementAt(error,index);	
	}
	
	/**
	 * This method clear the status catalog
	 */
	
	/**
	 * aceasta metoda reseteaza catalogul
	 */
	public void clearStatusCatalog(){
		
		set = 0;
	}
	
	/**
	 * This method calculate the number of finished jobs  
	 * @return the number of finished jobs
	 */
	public Integer nrOfFinishedJobs(){
		
		Integer nr = 0;
		for ( int i = 0; i < Nods.size(); i++ )
		{
			if(taskStatus.get(Nods.get(i).index)==4)
				nr++;
		}
		return nr;
	}
	
	/**
	 * This method calculate the number of jobs in error
	 * @return the number of jobs in error
	 */
	public Integer nrOfErrorJobs(){
		

		Integer nr = 0;
		for ( int i = 0; i < Nods.size(); i++ )
		{
			if(taskStatus.get(Nods.get(i).index)==0)
				nr++;
		}
		return nr;
	}
	

	
	/**
	 *  Numarul de joburi ce se doreste a se trece in starea de eroare 
	 *  nu trebuie sa depaseasca numarul nr
	 *  @param nr numarul de joburi ce pot trece in eroare
	 */
	
	/**
	 * This method simulates ...
	 * @param nr the maxim number of jobs in error
	 */
	public void simulateFaults_byNumber(Integer nr){
		Integer fin_jobs = nrOfFinishedJobs();
		Integer err_jobs = nrOfErrorJobs();
		Random rand = new Random();
		Integer task_err;
		if( ((taskStatus.size()-fin_jobs) > nr) && (err_jobs < nr) ){
			task_err = rand.nextInt(taskStatus.size());
			this.setStatusError(task_err);
		}
			
	}
	
	/**
	 * This method simulates ...
	 * @param nr the maxim number of jobs in error
	 */
	public void simulateFaults_byPercent(float p, float s){
		
		Integer fin_jobs = nrOfFinishedJobs();
		Integer err_jobs = nrOfErrorJobs();
		Integer nrt_jobs = taskStatus.size();
		Random rand = new Random();
		Integer task_err;
		Integer nr = Math.round((p/100)*nrt_jobs);
		if(aux < Math.round((s/100)*nrt_jobs)){
			aux++;
		}else{
			if( ((taskStatus.size()-fin_jobs) > nr) && (err_jobs < nr) ){
				task_err = rand.nextInt(taskStatus.size());
				this.setStatusError(task_err);
			}
			aux = 0;
		}
			
	}
	
	/**
	 * This method sets the algorithm to monitor
	 * @param algorithm
	 */
	public void setMonitorAlgorithm(String algorithm){
		monitor.setAlgorithm(algorithm);	
	}
	
	/**
	 * This method initialize the monitor
	 * @param algorithm
	 */
	public void initMonitor(String algorithm){
		monitor = new MonitorJob(this,tasks);
		monitor.setAlgorithm(algorithm);
	}
	
	/**
	 * This method starts the monitor
	 */
	public void startMonitor(){
		monitor.start();
		
	}
	
	/**
	 * 
	 * @param farm
	 */
	public void setFarm(Farm farm){
		monitor.setFarm(farm);
	}
	
	/**
	 * This method stops the monitor
	 */
	public void stopMonitor(){
		System.out.println("*****************Opresc monitorul******************");
		monitor.stop();
	}
	
	/**
	 *  This method prints the catalog
	 */
	public void printCatalog(){
		
		for ( int i = 0; i < Nods.size(); i++ )
		{
			System.out.println("nod "+Nods.get(i).index+" status"+taskStatus.get(Nods.get(i).index));
		}
		System.out.println();
		
	}
	
	/**
	 * This method return the first node in error
	 * @return the node in error
	 */
	public Nod getNodeInError(){
		for ( int i = 0; i < Nods.size(); i++ )
		{
			if(taskStatus.get(Nods.get(i).index)==0)
			{
				return Nods.get(i);
			}
		}
		return null;
	}
	
	/**
	 * A set of methods used for test. It sets in error always the same nodes for the test graph
	 */
	
	public void constructTestVec(){
		testVec = new Vector<Integer>();
		testVec.add(1);
		testVec.add(7);
		testVec.add(9);
		testVec.add(12);
	}
	
	public void testError(){
		if(aux < 3){
			aux++;
		}else{
			if(pos < testVec.size()){
				this.setStatusError(testVec.get(pos));
				pos++;
			}
			aux = 0;
		}
	}
}